function output = svm(ytra, Ktra, Ktest, C)
    % ytra: (Ntra x 1) vector, which contains class labels as -1 or +1
    % Ktra: (Ntra x Ntra) matrix, which contains kernel values between training instances
    % Ktest: (Ntest x Ntra) matrix, which contains kernel values between test instances and training instances
    % C: regularization parameter
    model = svm_train(ytra, Ktra, C); % trains SVM and stores model
    output = svm_test(Ktest, model); % outputs predictions for test instances
end

function model = svm_train(ytra, Ktra, C)
    N = size(Ktra, 1);
    yyKtra = (ytra * ytra') .* Ktra;
    alphas = zeros(N, 1);
    alphas = smo_solver(alphas, C, 1e-3, ytra, -ones(N, 1), yyKtra, 1e-3);
    alphas(alphas < C * 1e-3) = 0;
    alphas(alphas > C * (1 - 1e-3)) = C;
    support = find(alphas ~= 0);
    active = find(alphas ~= 0 & alphas < C);    
    model.alphas = alphas;
    if isempty(active) == 0
        model.b =  mean(ytra(active) .* (1 - yyKtra(active, support) * alphas(support)));
    else
        model.b = 0;
    end    
    model.y = ytra; 
end

function output = svm_test(Ktest, model)
    output = Ktest * (model.alphas .* model.y) + model.b;
end